﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ClientApp.Communication
{
    class Communication
    {

        public static Socket ConnectToServer(String ipAddr, int port)
        {
            try
            {
                //My ip address
                //Server's ip address
                IPAddress ipAddress = IPAddress.Parse(ipAddr);
                IPEndPoint remoteEp = new IPEndPoint(ipAddress, port);

                // Create a TCP/IP  socket.
                Socket sender = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    //connect to server
                    sender.Connect(remoteEp);
                    Console.WriteLine("Socket connected to {0}", sender.RemoteEndPoint.ToString());
                    return sender;
                }
                catch (Exception ee)
                {
                    Console.WriteLine("Error trying to connect to server. " + ee.ToString());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error whilst configuring stuff. " + e.ToString());
            }

            return null;
        }

        public static Socket BindToSocket(String ipAddr, int port)
        {
            IPAddress ipAddress = IPAddress.Parse(ipAddr);
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);

            // Create a TCP/IP socket.
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(10);

                return listener;
            }
            catch (Exception)
            {
                Console.WriteLine("Error While trying to bind to socket");
            }
            return null;
        }

        public static Boolean CloseConnectionWithServer(Socket soc)
        {
            try
            {
                // Release the socket.
                soc.Shutdown(SocketShutdown.Both);
                soc.Close();
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine("Error trying to close socket connection with server " + e.ToString());
            }
            return false;
        }

        public static String ListenForDataFromServer(Socket handler)
        {
            // Data buffer for incoming data.
            byte[] bytes = new Byte[1024];
            string data = null;

            try
            {
                // An incoming connection needs to be processed.
                while (true)
                {
                    bytes = new byte[1024];
                    int bytesRec = handler.Receive(bytes);
                    data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
                    if (data.IndexOf("<EOF>") > -1)
                    {
                        break;
                    }
                }
                // Show the data on the console.
                Console.WriteLine("Text received : {0}", data);
                return data;
            }
            catch (Exception)
            {
                Console.WriteLine("Error while trying to recieve data from client");
            }
            return null;
        }

        public static Boolean SendServerData(Socket sender, String message)
        {
            // Data buffer for incoming data.
            //  byte[] bytes = new byte[1024];

            try
            {
                // Encode the data string into a byte array.
                byte[] msg = Encoding.ASCII.GetBytes(message);
                // Send the data through the socket.
                int bytesSent = sender.Send(msg);

                return true;
            }
            catch (ArgumentNullException ane)
            {
                Console.WriteLine("ArgumentNullException : {0}", ane.ToString());
            }
            catch (SocketException se)
            {
                Console.WriteLine("SocketException : {0}", se.ToString());
            }
            catch (Exception e)
            {
                Console.WriteLine("Unexpected exception : {0}", e.ToString());
            }
            return false;
        }


        public static Socket ConnectToAClient(Socket listener)
        {
            try
            {
                Console.WriteLine("Waiting for a connection...");
                // Program is suspended while waiting for an incoming connection.
                Socket handler = listener.Accept();
                return handler;
            }
            catch (Exception)
            {
                Console.WriteLine("Error While trying to Accept connection with client");
            }
            return null;
        }

        public static Boolean CloseConnection(Socket handler)
        {
            try
            {
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
                return true;
            }
            catch (Exception)
            {
                Console.WriteLine("Error while trying to close connection with client");
            }
            return false;
        }
        


        private static bool SendCommandToServer(int p_command)
        {
            string peerName = UtilClasses.GeneralHelperFunctions.ParentDirectoryNameExtractor();
             string ipAddress = ConfigurationManager.AppSettings["IPAddress"];
            string port = ConfigurationManager.AppSettings["Port"];

            try 
            {
                Socket connectionToServer = null;
                while(connectionToServer == null)
                {
                    connectionToServer = ConnectToServer(ipAddress,Convert.ToInt32(port));
                    if(connectionToServer != null)
                    {
                        SendServerData(connectionToServer, Convert.ToString(p_command)+"|"+peerName + "<EOF>");
                        CloseConnection(connectionToServer);

                    }
                }
            }
            catch(Exception e)
            {
                return false;
            }

            return true;
        }


        public static bool SendCommandToServer(string command)
        {
            string ipAddress = ConfigurationManager.AppSettings["IPAddress"];
            string port = ConfigurationManager.AppSettings["Port"];

            try
            {
                Socket connectionToServer = null;
                while (connectionToServer == null)
                {
                    connectionToServer = ConnectToServer(ipAddress, 11000);
                    if (connectionToServer != null)
                    {
                        SendServerData(connectionToServer, command + "<EOF>");
                        CloseConnection(connectionToServer);
                    }
                }
            }
            catch (Exception e)
            {
                return false;
            }

            return true;
        }

        public static bool SendJobStarted()
        {
            bool succesCommand = SendCommandToServer((int)UtilClass.CommandsToSendList.CONFIRMATION_COMMAND);
            return succesCommand;
        }

        public static bool SendJobTerminated()
        {
            bool succesCommand = SendCommandToServer((int)UtilClass.CommandsToSendList.PROCESS_TERMINATED);
            return succesCommand;
        }

        public static bool SendPeerOnNetwork()
        {
            bool succesCommand = SendCommandToServer((int)UtilClass.CommandsToSendList.CLIENT_ON_NETWORK);
            return succesCommand;
        }

    }
}
